Skip to content

feat: version display + one-click upgrades via Watchtower#103

Open
MichaelCordner wants to merge 3 commits intomainfrom
feat/version-display-and-upgrades
Open

feat: version display + one-click upgrades via Watchtower#103
MichaelCordner wants to merge 3 commits intomainfrom
feat/version-display-and-upgrades

Conversation

@MichaelCordner
Copy link
Copy Markdown
Contributor

Summary

Adds version visibility in the UI and a one-click upgrade path so users never have to SSH in to update.

What changed

UI sidebar footer — always shows the current version (e.g. v0.2.5).

Update banner — when a newer release exists on GitHub:

  • If Watchtower is configured: shows an "Update now" button that triggers the upgrade in one click
  • If Watchtower is not configured: shows a "View release notes" link to the GitHub release page

POST /admin/upgrade endpoint — human session required. Calls Watchtower's HTTP API to trigger an image pull + container restart. Returns immediately; the app briefly goes offline and comes back on the new version. Agents cannot call this endpoint (403 for non-human sessions).

Watchtower sidecar in compose.yml — runs in monitor-only mode by default:

  • WATCHTOWER_MONITOR_ONLY=true — checks for new images daily but never applies them automatically
  • WATCHTOWER_HTTP_API_UPDATE=true — exposes an internal HTTP endpoint for on-demand updates
  • WATCHTOWER_SCOPE=jentic — scoped to only the jentic-mini container (won't touch other containers on the host)
  • No ports exposed to the host — only reachable from jentic-mini via the Docker network
  • Token-authenticated (WATCHTOWER_TOKEN env var, defaults to jentic-update)

/version endpoint — now returns upgrade_available: true/false so the UI knows whether to show the button or the fallback link.

jentic-update.sh — host-side fallback script for users who don't use the compose file with Watchtower: docker compose pull && docker compose up -d.

README — new "Updating" section documenting all three upgrade paths (UI button, script, manual commands).

Files changed

File Change
ui/src/components/layout/Layout.tsx Version display in sidebar footer + update banner with button
ui/src/hooks/useUpdateCheck.ts Added upgradeAvailable to UpdateStatus interface
compose.yml Watchtower sidecar service + scope labels + env vars on jentic-mini
src/main.py POST /admin/upgrade endpoint + upgrade_available field on /version
jentic-update.sh New file — host-side manual upgrade script
README.md New "Updating" section

Security notes

  • /admin/upgrade requires a human session (is_human_session check) — agents get 403
  • Watchtower's HTTP API is token-authenticated and only reachable on the internal Docker network
  • Docker socket is mounted only into the Watchtower container, not into jentic-mini
  • Watchtower is scoped — it only monitors/updates the jentic-mini container

How to test

  1. docker compose up -d — both jentic-mini and watchtower start
  2. Log in to the UI — version number visible at bottom of sidebar
  3. The update banner appears if a newer GitHub release exists (you can test by setting APP_VERSION=0.0.1 as a build arg)
  4. Click "Update now" — calls /admin/upgrade, Watchtower pulls the latest image and restarts the container
  5. Without Watchtower: stop it with docker compose stop watchtower, verify the banner falls back to showing the release link instead of the button

🤖 Generated with Claude Code

MichaelCordner and others added 3 commits March 29, 2026 15:24
- Always show current version in sidebar footer
- Watchtower sidecar in monitor-only + HTTP API mode (checks daily, never auto-updates)
- POST /admin/upgrade endpoint (human session required) triggers Watchtower to pull + restart
- /version endpoint returns upgrade_available flag
- UI banner shows "Update now" button when Watchtower is configured, falls back to release link
- jentic-update.sh host-side fallback script
- README documenting update options

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The sessionStorage cache persisted across upgrades, showing a stale
"update available" banner even after the update was applied. Now the
cache is validated against /health on load — if the running version
differs from the cached currentVersion, the cache is discarded and
a fresh check runs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@char0n char0n self-assigned this Mar 31, 2026
@char0n char0n added the enhancement New feature or request label Mar 31, 2026
@char0n char0n requested a review from Copilot March 31, 2026 13:46
Copy link
Copy Markdown
Member

@char0n char0n left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

Good feature — version visibility + one-click upgrades with correct security model (human-only, token-authenticated Watchtower, scoped to jentic container). Four items to fix before merge:

Must fix

1. Missing credentials: 'include' on upgrade fetch (Layout.tsx)

await fetch('/admin/upgrade', { method: 'POST' })

No session cookie sent → endpoint always returns 403. Should be:

await fetch('/admin/upgrade', { method: 'POST', credentials: 'include' })

2. Watchtower response not checked (main.py:288)

The response from Watchtower is ignored — a 401 (bad token) or 500 still returns "status": "update_triggered". Should check resp.status_code and return an error if Watchtower rejects the request.

3. upgrade_available is misleading (main.py:262)

"upgrade_available": bool(os.getenv("WATCHTOWER_API_URL")),

Returns true if Watchtower is configured, not if an upgrade exists. The UI uses this to decide between "Update now" button vs "View release notes" link. Rename to watchtower_configured or upgrade_mechanism_available.

4. Hardcoded amber colors (Layout.tsx)

PR #83 replaced all hardcoded Tailwind colors with semantic design tokens. This reintroduces amber-* classes (text-amber-600, bg-amber-50, etc.). Should use text-warning, bg-warning/10 or the accent-yellow tokens from index.css.

Nice to have

5. Duplicate from fastapi import HTTPException — imported inline 3x in trigger_upgrade() but already imported at top of main.py.

6. Cache flashuseUpdateCheck.ts sets stale cached status immediately, then fires async /health check to validate. User briefly sees old "update available" banner before correction. Minor UX.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds UI version visibility and an update/upgrade flow driven by backend /version metadata and a new human-only /admin/upgrade endpoint, with a Watchtower sidecar provided via compose.yml plus a CLI fallback script.

Changes:

  • UI: display current version in the sidebar footer and show an “update available” banner with either “Update now” (Watchtower) or “View release notes”.
  • Backend: extend /version with upgrade_available and add POST /admin/upgrade to trigger a Watchtower HTTP API update.
  • Deployment/docs: add Watchtower sidecar configuration, a jentic-update.sh host script, and a README “Updating” section.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
ui/src/hooks/useUpdateCheck.ts Adds upgradeAvailable and adjusts sessionStorage caching + version validation logic
ui/src/components/layout/Layout.tsx Adds sidebar version footer and update banner/button UI
src/main.py Adds upgrade_available on /version and introduces /admin/upgrade Watchtower trigger endpoint
compose.yml Adds Watchtower sidecar service and scopes/vars to enable on-demand updates
jentic-update.sh Adds a host-side manual update script using docker compose pull/up
README.md Documents update options (UI/Watchtower, script, manual)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@char0n
Copy link
Copy Markdown
Member

char0n commented Mar 31, 2026

Additional items from Copilot review

Three more worth addressing:

7. Pin Watchtower version (compose.yml:29)

containrrr/watchtower:latest is non-deterministic. A breaking Watchtower release could silently break the upgrade path. Pin to a specific version (e.g., containrrr/watchtower:1.7.1).

8. Upgrade button stuck on error (Layout.tsx:103)

setUpgrading(true) is never reset if /admin/upgrade returns a non-2xx (Watchtower down, bad token, 502). The button stays permanently disabled showing "Updating...". Needs a .then() / .catch() to reset state on failure.

9. currentVersion never set when telemetry is off (useUpdateCheck.ts:55)

check() returns early when latestVersion is missing, which means currentVersion is never stored in state either. The sidebar version display (v{currentVersion}) stays blank when JENTIC_TELEMETRY=off. Should set currentVersion regardless of whether latestVersion exists.

Also noted by Copilot but lower priority:

  • Use Depends(require_human_session) instead of inline check for consistent auth pattern
  • Include upgrade_available in the telemetry-off response path for consistent response shape

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants